package com.hzw.saas.service.old.storage.service;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzw.saas.api.old.storage.ISysResourceService;
import com.hzw.saas.api.old.storage.bo.SysResourceBO;
import com.hzw.saas.api.old.storage.bo.SysResourceDataBO;
import com.hzw.saas.api.old.storage.enums.ResourceStatusEnum;
import com.hzw.saas.common.config.exception.DefaultSaasException;
import com.hzw.saas.common.config.util.AssertUtil;
import com.hzw.saas.common.util.IdWorker;
import com.hzw.saas.service.old.hub.service.IS3FileService;
import com.hzw.saas.service.old.storage.mapper.OldSysResourceMapper;
import com.hzw.saas.service.old.storage.model.SysResource;
import com.hzw.saas.service.storage.config.StorageConfig;
import cn.hutool.core.io.FileUtil;
import cn.hutool.crypto.digest.DigestUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import ma.glasnost.orika.MapperFacade;


/**
 * @author zzl
 * @since 03/01/2021
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class OldSysResourceServiceImpl extends ServiceImpl<OldSysResourceMapper, SysResource> implements ISysResourceService, IService<SysResource> {

    private final MapperFacade mapperFacade;
    private final IdWorker idWorker;
    private final StorageConfig storageConfig;
    private final IS3FileService fileService;

    @Override
    public boolean exists(String userId, String resourceName) {
        SysResourceBO sysResourceBO = this.get(userId, resourceName);
        if (sysResourceBO == null)
            return false;
        return fileService.exists(sysResourceBO.getResourceKey());
    }

    @Override
    public SysResourceBO get(String userId, String resourceName) {
        SysResource sysResource = this.lambdaQuery().eq(SysResource::getCreatorId, userId).eq(SysResource::getName, resourceName).one();
        return mapperFacade.map(sysResource, SysResourceBO.class);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SysResourceBO add(SysResourceDataBO resourceDataDto) {
        if (exists(resourceDataDto.getCreatorId(), resourceDataDto.getName())) {
            this.update(resourceDataDto);
        }
        log.debug("提交资源{userId: {}, name: {}}...", resourceDataDto.getCreatorId(), resourceDataDto.getName());
        AssertUtil.assertThrow("资源名称不可为空", StringUtils.isBlank(resourceDataDto.getName()));
        boolean uploadFileExists = resourceDataDto.getFile().getResource().exists();

        if (StringUtils.isBlank(resourceDataDto.getFileId()))
            resourceDataDto.setFileId(String.valueOf(idWorker.nextId()));
        if (resourceDataDto.getStatus() == null)
            resourceDataDto.setStatus(ResourceStatusEnum.NORMAL.code());
        if (StringUtils.isBlank(resourceDataDto.getResourceKey()) && uploadFileExists) {
            try {
                String sha1 = DigestUtil.sha1Hex(resourceDataDto.getFile().getBytes());
                resourceDataDto.setResourceKey(sha1);
            } catch (IOException e) {
                log.error(String.format("提交资源{userId: %s, name: %s}失败", resourceDataDto.getCreatorId(), resourceDataDto.getName()), e);
                throw DefaultSaasException.build("提交资源失败：\r\n" + e.getMessage());
            }
        }
        if (uploadFileExists) {
            resourceDataDto.setLength(resourceDataDto.getFile().getSize());
        }
        if (resourceDataDto.getCreateDate() == null)
            resourceDataDto.setCreateDate(new Date());
        SysResourceBO sysResourceBO = mapperFacade.map(resourceDataDto, SysResourceBO.class);
        // 持久化信息
        SysResource sysResource = mapperFacade.map(resourceDataDto, SysResource.class);
        this.save(sysResource);
        // 上传文件至s3
        this.uploadFile(resourceDataDto);
        return sysResourceBO;
    }

    @Override
    public void update(SysResourceDataBO resourceDataDto) {
        AssertUtil.assertThrow("资源ID为空", StringUtils.isBlank(resourceDataDto.getFileId()));
        AssertUtil.assertThrow("资源名称不可为空", resourceDataDto.getName() != null && StringUtils.isBlank(resourceDataDto.getName()));
        SysResource sysResource = mapperFacade.map(resourceDataDto, SysResource.class);
        this.updateById(sysResource);
        SysResource now = this.getById(resourceDataDto.getFileId());
        resourceDataDto.setResourceKey(now.getResourceKey());
        this.uploadFile(resourceDataDto);
    }

    @Override
    public void download(String userId, String resourceName, OutputStream ous) {
        AssertUtil.assertThrow("资源不存在", !this.exists(userId, resourceName));
        log.debug("开始下载资源{userId: {}, name: {}}", userId, resourceName);
        SysResourceBO resourceDto = this.get(userId, resourceName);
        try {
            fileService.downloadFile(resourceDto.getResourceKey(), ous);
            log.debug("下载资源{userId: {}, name: {}}完成", userId, resourceName);
        } catch (IOException e) {
            String errorMsg = String.format("下载资源{userId: %s, name: %s}失败", userId, resourceName);
            log.debug(errorMsg, e);
            throw DefaultSaasException.build(errorMsg);
        }
    }

    private void uploadFile(SysResourceDataBO resourceDataDto) {
        // 上传文件至s3
        if (resourceDataDto.getFile().getResource().exists()) {
            try {
                log.debug("上传资源{userId: {}, name: {}}至S3...", resourceDataDto.getCreatorId(), resourceDataDto.getName());
                File file = FileUtil.file(storageConfig.getFolderPath(), "sys", resourceDataDto.getFileId());
                FileUtil.writeBytes(resourceDataDto.getFile().getBytes(), file);
                fileService.uploadFile(file, resourceDataDto.getResourceKey());
            } catch (IOException e) {
                log.error(String.format("上传资源{userId: %s, name: %s}至S3失败", resourceDataDto.getCreatorId(), resourceDataDto.getName()), e);
                throw DefaultSaasException.build("提交资源失败：\r\n" + e.getMessage());
            }
        }
    }
}
